/*
Copyright (C) 1994-1995 Apogee Software, Ltd.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
#include "rt_def.h"
#include "rt_sound.h"
#include "_rt_soun.h"
#include "fx_man.h"
#include "music.h"
#include "z_zone.h"
#include "w_wad.h"
#include "rt_main.h"
#include "rt_ted.h"
#include "rt_menu.h"
#include "rt_playr.h"
#include "rt_util.h"
#include "rt_rand.h"
#include "watcom.h"
#include <stdio.h>
#include <stdlib.h>
#include <mem.h>
#include <io.h>
#include "rt_cfg.h"
#include "isr.h"
#include "develop.h"
#include "rt_net.h"

#include "rt_str.h"

#if (SHAREWARE==0)
#include "snd_reg.h"
#else
#include "snd_shar.h"
#endif
//MED
#include "memcheck.h"

// Local Variables

static soundstart;
static soundtype;
int SD_Started=false;
static boolean PositionStored=false;
static int NumBadSounds=0;
static int remotestart;
static boolean SoundsRemapped = false;

int musicnums[ 11 ] = {
   -1, UltraSound, SoundBlaster, SoundMan16, ProAudioSpectrum,
   Awe32, SoundScape, WaveBlaster, GenMidi, SoundCanvas, Adlib
   };

int fxnums[ 11 ] = {
   -1, UltraSound, SoundBlaster, SoundMan16, ProAudioSpectrum,
   Awe32, SoundScape, Adlib, SoundSource, TandySoundSource, PC
   };

int MUSIC_GetPosition( void ) {
   songposition pos;

   MUSIC_GetSongPosition( &pos );
   return pos.milliseconds;
}

void MUSIC_SetPosition( int time ) {
   MUSIC_SetSongTime( ( unsigned long )time );
}


//***************************************************************************
//
// SoundNumber
//
//***************************************************************************

int SoundNumber ( int x )
{
   if ((x>=SD_REMOTEM1SND) && (x<=SD_REMOTEM10SND))
      return remotestart + x - SD_REMOTEM1SND;
//      sounds[x].snds[soundtype]+remotestart;
   else
      return sounds[x].snds[soundtype]+soundstart;
}


//***************************************************************************
//
// SD_MakeCacheable - Make a sound that has just finished playing cacheable again
//
//***************************************************************************
void SD_MakeCacheable( unsigned long sndnum )
{
   if (sndnum == (unsigned long) -1)
      {
      return;
      }

   if (sndnum>=MAXSOUNDS)
      {
      SoftError ("Illegal sound value in SD_MakeCacheable value=%ld\n",sndnum);
      return;
      }
   sounds[sndnum].count--;
   if (sounds[sndnum].count>0)
      return;
   else
      W_CacheLumpNum(SoundNumber(sndnum),PU_CACHE);
}

#if 0
//***************************************************************************
//
// SD_PrintActive
//
//***************************************************************************
void SD_PrintActive ( void )
{
   int i;

   myprintf("Active Sounds\n");
   for (i=0;i<MAXSOUNDS;i++)
      {
      if (sounds[i].count>0)
         {
         myprintf("sound active #%ld\n",i);
         }
      }
}
#endif

//***************************************************************************
//
// SD_SetupFXCard - Initialize sound Tables and start up sound card
//
//***************************************************************************

int SD_SetupFXCard ( int * numvoices, int * numbits, int * numchannels)
{
   fx_device device;
   int status;
   int card;

   if (SD_Started==true)
      SD_Shutdown();

   if ( ( FXMode < 0 ) || ( FXMode >= 11 ) )
      {
      return( 0 );
      }

   card = fxnums[ FXMode ];
   if (card==-1) // Check if it is off
      return (0);
   if ( ( card == SoundBlaster ) || ( card == Awe32 ) )
      {
      extern fx_blaster_config SBSettings;

      status = FX_SetupSoundBlaster( SBSettings, numvoices,
         numbits, numchannels );
      }
   else
      {
      status=FX_SetupCard( card, &device );
      if ( status == FX_Ok )
         {
         *numvoices=device.MaxVoices;
         *numbits=device.MaxSampleBits;
         *numchannels=device.MaxChannels;
         }
      }

   return (status);
   }

//***************************************************************************
//
// SD_Startup - Initialize sound Tables and start up sound card
//
//***************************************************************************

int SD_Startup ( boolean bombonerror )
{
   int status;
   int card;
   int voices;
   int channels;
   int bits;
   int i;
   extern boolean IS8250;

   if (SD_Started==true)
      SD_Shutdown();

   if ( ( FXMode < 0 ) || ( FXMode >= 11 ) )
      {
      return( 0 );
      }
   card = fxnums[ FXMode ];
   if (card==-1) // Check if it is off
      return (0);

   switch (card)
      {
      case UltraSound:
      case SoundBlaster:
      case SoundMan16:
      case ProAudioSpectrum:
      case Awe32:
      case SoundSource:
      case TandySoundSource:
      case SoundScape:
         soundstart=W_GetNumForName("digistrt")+1;
         soundtype=fx_digital;
         break;
      case Adlib:
         soundstart=W_GetNumForName("adstart")+1;
         soundtype=fx_muse;
         break;
      case PC:
         soundstart=W_GetNumForName("pcstart")+1;
         soundtype=fx_muse;
         break;
      default:
         Error("FX: Unsupported Card number %d",FXMode);
         break;
      }

   if ( soundtype == fx_digital )
      {
      if ( SoundsRemapped == false )
         {
         for( i = 0; i < SD_LASTSOUND; i++ )
            {
            int snd;

            snd = sounds[ i ].snds[ fx_digital ];
            if ( snd >= 0)
               {
               sounds[ i ].snds[ fx_digital ] = W_GetNumForName(
                  W_GetNameForNum( snd + soundstart ) );
               }
            }
         SoundsRemapped = true;
         }
      soundstart = 0;
      }

   voices   = NumVoices;
   channels = NumChannels;
   bits     = NumBits;

   if ( IS8250 )
      {
      voices   = max( voices, 4 );
      channels = 1;
      bits     = 8;
      }

   status=FX_Init( card, voices, channels, bits, 11000 );

   if (status != FX_Ok)
      {
      if (bombonerror)
         {
         DeleteSoundFile ();
         Error( "%s\n", FX_ErrorString( status ) );
         }

      return (status);
      }

   if (stereoreversed == true)
      {
      FX_SetReverseStereo(!FX_GetReverseStereo());
      }

   FX_SetCallBack( SD_MakeCacheable );

   remotestart=W_GetNumForName("remostrt")+1;

   SD_Started=true;

   FX_SetVolume (FXvolume);

   return (0);
}

//***************************************************************************
//
// SD_SoundOkay - checks to see if the sound is okay
//
//***************************************************************************

boolean SD_SoundOkay ( int sndnum )
{
   if (SD_Started==false)
      return false;

   if (sndnum>=MAXSOUNDS)
      Error ("Illegal sound number, sound number = %d\n",sndnum);

   if (SoundOffset(sndnum)==-1)
      return false;

   if ( ( sounds[ sndnum ].flags & SD_PLAYONCE ) &&
      ( SD_SoundActive( sounds[ sndnum ].prevhandle ) ) )
      {
      return false;
      }

   return true;
}

//***************************************************************************
//
// SD_PlayIt - Play a pre-setup sound
//
//***************************************************************************

int SD_PlayIt ( int sndnum, int angle, int distance, int pitch )
{
   int voice;
   byte * snd;

#if (DEVELOPMENT == 1)
#if (SOUNDTEST == 1)
   SoftError("SOUND =%d \n",sndnum);
#endif
#endif

   if (!(sounds[sndnum].flags & SD_WRITE))
      {
      if (sounds[sndnum].count)
         {
         if (distance<=sounds[sndnum].prevdistance)
            FX_StopSound(sounds[sndnum].prevhandle);
         else
            return 0;
         }
      }

   if ( !FX_VoiceAvailable( sounds[sndnum].priority ) )
      {
      return( 0 );
      }

   sounds[sndnum].count++;

   snd=W_CacheLumpNum(SoundNumber(sndnum),PU_STATIC);

   if ( *snd == 'C' )
      {
      voice = FX_PlayVOC3D( snd, pitch, angle, distance,
         sounds[sndnum].priority, (unsigned long) sndnum );
      }
   else
      {
      voice = FX_PlayWAV3D( snd, pitch, angle, distance,
         sounds[sndnum].priority, (unsigned long) sndnum );
      }

   if ( voice < FX_Ok )
      {
#if (DEVELOPMENT == 1)
/*
      if (MV_ErrorCode == MV_InvalidVOCFile)
         {
         Error("SD_PlayIt: Invalid VOC File snd=%p sndnum=%ld lump=%ld\n",snd,sndnum,SoundNumber(sndnum));
         }
*/
      NumBadSounds++;
      SoftError("SD_PlayIt: Error/Warning %s\n",FX_ErrorString( FX_Error ));
      SoftError("BadSoundNumber %ld time %ld\n",NumBadSounds,ticcount);
#endif
      SD_MakeCacheable( sndnum );

      return 0;
      }

   NumBadSounds=0;

   if (!(sounds[sndnum].flags & SD_WRITE))
      {
      sounds[sndnum].prevhandle=voice;
      sounds[sndnum].prevdistance=distance;
      }
   return voice;
}


//***************************************************************************
//
// SD_Play - Play a sample
//
//***************************************************************************

int SD_Play ( int sndnum )
{
   int voice;
   int pitch;

   if ( SD_SoundOkay ( sndnum ) == false )
      return 0;

   pitch = 0;

   if ( !( sounds[ sndnum ].flags & SD_PITCHSHIFTOFF ) )
      {
      pitch = PitchOffset();
      }

   voice = SD_PlayIt ( sndnum, 0, 0, pitch );

   return voice;

}

//***************************************************************************
//
// SD_Play3D - Play a positioned sample
//
//***************************************************************************

int SD_Play3D ( int sndnum, int angle, int distance )
{
   int voice;
   int pitch;

   if ( SD_SoundOkay ( sndnum ) == false )
      return 0;

   pitch = 0;
   if ( !( sounds[ sndnum ].flags & SD_PITCHSHIFTOFF ) )
      {
      pitch = PitchOffset();
      }

   voice = SD_PlayIt ( sndnum, angle, distance, pitch );

   return voice;

}

//***************************************************************************
//
// SD_PlayPositionedSound - Play a positioned sample
//
//***************************************************************************

int SD_PlayPositionedSound ( int sndnum, int px, int py, int x, int y )
{
   int voice;
   int angle;
   int distance;
   int dx;
   int dy;
   int pitch;

   if ( SD_SoundOkay ( sndnum ) == false )
      return 0;

   dx=(x-px);
   dy=(py-y);

   distance=FindDistance(dx,dy) >> SD_DISTANCESHIFT;

   if (distance>255)
      return 0;

   if (distance!=0)
      {
      angle = ( atan2_appx(dx,dy) & (FINEANGLES-1) ) >> 6;
      }
   else
      {
      angle=0;
      }

   pitch = 0;

   if ( !( sounds[ sndnum ].flags & SD_PITCHSHIFTOFF ) )
      {
      pitch = PitchOffset();
      }

   voice = SD_PlayIt ( sndnum, angle, distance, pitch );

   return voice;

}

//***************************************************************************
//
// SD_PlaySoundRTP - Play a positioned sample relative to the player
//
//***************************************************************************

int SD_PlaySoundRTP ( int sndnum, int x, int y )
{
   int voice;
   int angle;
   int distance;
   int dx;
   int dy;
   int pitch;


   if ( SD_SoundOkay ( sndnum ) == false )
      return 0;

   dx=(x-player->x);
   dy=(player->y-y);

   distance=FindDistance(dx,dy) >> SD_DISTANCESHIFT;

   if (distance>255)
      return 0;

   if (distance!=0)
      {
      angle = ( (player->angle - atan2_appx(dx,dy)) & (FINEANGLES-1) ) >> 6;
      }
   else
      {
      angle=0;
      }

   pitch = 0;

   if ( !( sounds[ sndnum ].flags & SD_PITCHSHIFTOFF ) )
      {
      pitch = PitchOffset();
      }

   voice = SD_PlayIt ( sndnum, angle, distance, pitch );

   return voice;
}

//***************************************************************************
//
// SD_PlayPitchedSound - Play a pitched sample
//
//***************************************************************************

int SD_PlayPitchedSound ( int sndnum, int volume, int pitch )
{
   int voice;
   int distance;

   if ( SD_SoundOkay ( sndnum ) == false )
      return 0;

   distance = 255 - volume;

   voice = SD_PlayIt ( sndnum, 0, distance, pitch );

   return voice;
}

//***************************************************************************
//
// SD_SetSoundPitch - sets the pitch of a sound
//
//***************************************************************************

void SD_SetSoundPitch ( int sndnum, int pitch )
{
   int status;

   if (SD_Started==false)
      return;

   if (!FX_SoundActive(sndnum))
      return;

   status=FX_SetPitch( sndnum, pitch );
   if (status != FX_Ok)
      {
#if (DEVELOPMENT == 1)
      SoftError("SD_SetSoundPitch : %s\n",FX_ErrorString( status ));
#endif
      }
}

//***************************************************************************
//
// SD_PanRTP Sound - pan a positioned sample relative to the player
//
//***************************************************************************

void SD_PanRTP ( int handle, int x, int y )
{
   int angle;
   int distance;
   int dx;
   int dy;
   int status;

   if (SD_Started==false)
      return;

   if (!FX_SoundActive(handle))
      return;

   dx=(x-player->x);
   dy=(player->y-y);

   distance=FindDistance(dx,dy) >> SD_DISTANCESHIFT;

   if (distance>255)
      return;

   if (distance!=0)
      {
      angle = ( (player->angle - atan2_appx(dx,dy)) & (FINEANGLES-1) ) >> 6;
      }
   else
      {
      angle = 0;
      }

   status = FX_Pan3D ( handle, angle, distance );

   if (status != FX_Ok)
      {
#if (DEVELOPMENT == 1)
      SoftError("SD_PanPositionedSound: %s\n",FX_ErrorString( status ));
#endif
      }
}

//***************************************************************************
//
// SD_SetPan - set the pan of a sample
//
//***************************************************************************

void SD_SetPan ( int handle, int vol, int left, int right )
{
   int status;

   if (SD_Started==false)
      return;

   if (!FX_SoundActive(handle))
      return;

   status=FX_SetPan( handle, vol, left, right );

   if (status != FX_Ok)
      {
#if (DEVELOPMENT == 1)
      SoftError("SD_SetPan: %s\n",FX_ErrorString( status ));
#endif
      }
}

//***************************************************************************
//
// SD_PanPositioned Sound - pan a positioned sample
//
//***************************************************************************

void SD_PanPositionedSound ( int handle, int px, int py, int x, int y )
{
   int angle;
   int distance;
   int dx;
   int dy;
   int status;

   if (SD_Started==false)
      return;

   if (!FX_SoundActive(handle))
      return;

   dx=(x-px);
   dy=(py-y);

   distance=FindDistance(dx,dy) >> SD_DISTANCESHIFT;

   if (distance>255)
      return;

   if (distance!=0)
      {
      angle = ( atan2_appx(dx,dy) & (FINEANGLES-1) ) >> 6;
      }
   else
      {
      angle = 0;
      }

   status=FX_Pan3D( handle, angle, distance );

   if (status != FX_Ok)
      {
#if (DEVELOPMENT == 1)
      SoftError("SD_PanPositionedSound: %s\n",FX_ErrorString( status ));
#endif
      }
}


//***************************************************************************
//
// SD_StopSound - Stop the current sound from playing
//
//***************************************************************************

void SD_StopSound ( int handle )
{
   int status;

   if (SD_Started==false)
      return;

   status=FX_StopSound( handle);

   if (status != FX_Ok)
      {
#if (DEVELOPMENT == 1)
      SoftError("SD_StopSound: %s\n",FX_ErrorString( status ));
#endif
      }
}

//***************************************************************************
//
// SD_StopAllSounds - Stop All the sounds currently playing
//
//***************************************************************************

void  SD_StopAllSounds ( void )
{
   int status;

   if (SD_Started==false)
      return;

   status=FX_StopAllSounds();

   if (status != FX_Ok)
      {
#if (DEVELOPMENT == 1)
      SoftError("SD_StopAllSounds: %s\n",FX_ErrorString( status ));
#endif
      }
}

//***************************************************************************
//
// SD_SoundActive - See if a sound is active
//
//***************************************************************************

int SD_SoundActive ( int handle )
{
   if (SD_Started==false)
      {
      return false;
      }
   else
      {
      return (FX_SoundActive(handle));
      }
}

//***************************************************************************
//
// SD_WaitSound - wait until a sound has finished
//
//***************************************************************************
void SD_WaitSound ( int handle )
{
   int time;

   IN_ClearKeysDown();

   while (FX_SoundActive(handle)!=0)
      {
      time=ticcount+1;
      while (time>ticcount) {}
      if ((LastScan) || IN_GetMouseButtons())
         break;
      }
}


//***************************************************************************
//
// SD_Shutdown - Shutdown the sound system
//
//***************************************************************************

void SD_Shutdown (void)
{
   if (SD_Started==false)
      return;

   FX_Shutdown();
   SD_Started=false;
}


//***************************************************************************
//
// SD_PreCacheSound - PreCache sound
//
//***************************************************************************

void SD_PreCacheSound ( int num )
{
   if ( SD_SoundOkay ( num ) == false )
      return;

   PreCacheLump(SoundNumber(num),PU_CACHESOUNDS+sounds[num].priority);
}

//***************************************************************************
//
// SD_PreCacheSoundGroup - PreCache sound group
//
//***************************************************************************

void SD_PreCacheSoundGroup ( int lo, int hi )
{
   int i;

   if (SD_Started==false)
      return;

   for (i=lo;i<=hi;i++)
      SD_PreCacheSound(i);
}


#if (SHAREWARE == 1)
#define MAXSONGS 18
static song_t rottsongs[MAXSONGS] = {
      { loop_no,  song_apogee  ,"FANFARE2","Apogee Fanfare"},
      { loop_yes, song_title   ,"RISE",    "Rise"},
      { loop_yes, song_menu    ,"MMMENU",  "MMMenu"},
      { loop_yes, song_christmas,"DEADLY", "Deadly Gentlemen"},
      { loop_yes, song_elevator,"GOINGUP", "Going up?"},
      { loop_yes, song_endlevel,"HOWDIDO", "How'd I do?"},
      { loop_yes, song_secretmenu,"FISHPOLK","Fish Polka"},
      { loop_yes, song_gameover,"YOUSUCK", "You Suck"},
      { loop_yes, song_youwin  ,"WATZNEXT","Watz Next?"},
      { loop_no,  song_gason   ,"GAZZ!",   "Gazz!"},
      { loop_yes, song_level   ,"FASTWAY", "Goin' Down The Fast Way"},
      { loop_yes, song_level   ,"MISTACHE","Mist Ache"},
      { loop_yes, song_level   ,"OWW",     "Oww!!!"},
      { loop_yes, song_level   ,"SMOKE",   "Smoke And Mirrors"},
      { loop_yes, song_level   ,"SPRAY",   "Spray"},
      { loop_yes, song_level   ,"RUNLIKE", "Run Like Smeg"},
      { loop_yes, song_level   ,"SMOOTH",  "Havana Smooth"},
      { loop_yes, song_level   ,"CHANT",   "Chant"},
      };
#else
#define MAXSONGS 34
static song_t rottsongs[MAXSONGS] = {
      { loop_no,  song_apogee  ,"FANFARE2","Apogee Fanfare"},
      { loop_yes, song_title   ,"RISE",    "Rise"},
      { loop_yes, song_menu    ,"MMMENU",  "MMMenu"},
      { loop_yes, song_christmas,"DEADLY", "Deadly Gentlemen"},
      { loop_yes, song_elevator,"GOINGUP", "Going up?"},
      { loop_yes, song_secretmenu,"FISHPOLK","Fish Polka"},
      { loop_yes, song_endlevel,"HOWDIDO", "How'd I do?"},
      { loop_yes, song_gameover,"YOUSUCK", "You Suck"},
      { loop_yes, song_cinematic2,"WATZNEXT","Watz Next?"},
      { loop_no,  song_gason   ,"GAZZ!",   "Gazz!"},
      { loop_yes, song_level   ,"FASTWAY", "Goin' Down The Fast Way"},
      { loop_yes, song_level   ,"MISTACHE","Mist Ache"},
      { loop_yes, song_level   ,"OWW",     "Oww!!!"},
      { loop_yes, song_level   ,"SMOKE",   "Smoke And Mirrors"},
      { loop_yes, song_level   ,"SPRAY",   "Spray"},
      { loop_yes, song_level   ,"RUNLIKE", "Run Like Smeg"},
      { loop_yes, song_level   ,"SMOOTH",  "Havana Smooth"},
      { loop_yes, song_level   ,"CHANT",   "Chant"},
      { loop_yes, song_level   ,"MEDIEV1A","Funeral of Queen Mary"},
      { loop_yes, song_level   ,"TASKFORC","Task Force"},
      { loop_yes, song_level   ,"KISSOFF", "KISS Off"},
      { loop_yes, song_level   ,"RADAGIO", "Adagio For Strings"},
      { loop_yes, song_level   ,"SHARDS",  "Shards"},
      { loop_yes, song_level   ,"STAIRS",  "I Choose the Stairs"},
      { loop_yes, song_level   ,"SUCKTHIS","Suck This"},
      { loop_yes, song_level   ,"EXCALIBR","Excalibur"},
      { loop_yes, song_level   ,"CCCOOL",   "CCCool"},
      { loop_yes, song_level   ,"WORK_DAY","Work Day"},
      { loop_yes, song_level   ,"WHERIZIT","Where Iz It?"},
      { loop_no,  song_bossdie,"BOSSBLOW", "Boss Blow"},
      { loop_yes, song_bosssee ,"HELLERO", "Hellero"},
      { loop_yes, song_cinematic1,"EVINRUDE","Evin Rude"},
      { loop_yes, song_youwin  ,"VICTORY", "Victory!"},
      { loop_yes, song_dogend  ,"HERE_BOY","Here Boy"}
      };
#endif

static byte * currentsong;
static int MU_Started=false;
static int lastsongnumber=-1;
int storedposition=0;

//****************************************************************************
//
// MU_JukeBoxMenu()
//
//****************************************************************************

void MU_PlayJukeBoxSong
   (
   int which
   )

   {
   if ( ( MusicMode > 0 ) && ( MU_Started == true ) )
      {
      SetMenuHeader( rottsongs[ which ].songname );
      MU_PlaySong( which );
      }
   }


//****************************************************************************
//
// MU_JukeBoxMenu()
//
//****************************************************************************

void MU_JukeBoxRedraw
   (
   void
   )

   {
   if ( ( MusicMode > 0 ) && ( MU_Started == true ) )
      {
      SetMenuHeader( rottsongs[ lastsongnumber ].songname );
      }
   }


//****************************************************************************
//
// MU_JukeBoxMenu()
//
//****************************************************************************

void MU_JukeBoxMenu
   (
   void
   )

   {
   char *SongNames[ MAXSONGS ];
   int   i;

   for( i = 0; i < MAXSONGS; i++ )
      {
      SongNames[ i ] = rottsongs[ i ].songname;
      }

   HandleMultiPageCustomMenu( SongNames, MAXSONGS, lastsongnumber,
      "Jukebox", MU_PlayJukeBoxSong, MU_JukeBoxRedraw, false );

   if ( rottsongs[ lastsongnumber ].loopflag == loop_no )
      {
      MU_StartSong(song_level);
      }
   }

//***************************************************************************
//
// MusicStarted - see if the music is started
//
//***************************************************************************
boolean MusicStarted( void )
{
   return MU_Started;
}

//***************************************************************************
//
// MU_Startup - Initialize music stuff
//
//***************************************************************************

int MU_Startup ( boolean bombonerror )
{
   int status;
   int card;

   if (MU_Started==true)
      {
      MU_StopSong();
      MU_Shutdown();
      }
   if ( ( MusicMode < 0 ) || ( MusicMode >= 11 ) )
      {
      return( 0 );
      }
   card = musicnums[ MusicMode ];
   if (card==-1) // Check if it is off
      return (0);

   if ( ( card == SoundBlaster ) || ( card == Awe32 ) || ( card == WaveBlaster ) )
      {
      if ( SD_Started == false )
         {
         extern fx_blaster_config SBSettings;
         int numvoices;
         int numbits;
         int numchannels;

         FX_SetupSoundBlaster( SBSettings, &numvoices,
            &numbits, &numchannels );
         }
      }

   if (card== UltraSound)
      {
      MU_SetupGUSInitFile();
      }

   status=MUSIC_Init( card, MidiAddress );

   if (status != MUSIC_Ok)
      if (bombonerror)
         {
         DeleteSoundFile ();
         Error( "%s\n", MUSIC_ErrorString( status ) );
         }
      else
         return (status);

   currentsong=0;

   MU_Started=true;

   MU_SetVolume (MUvolume);

   return (0);
}

//***************************************************************************
//
// MU_Shutdown - Shutdown the music system
//
//***************************************************************************

void MU_Shutdown (void)
{
   if (MU_Started==false)
      return;
   MUSIC_Shutdown();
   MU_Started=false;
}
//***************************************************************************
//
// MU_SetupGUSInitFile - initialize GUS stuff
//
//***************************************************************************

void MU_SetupGUSInitFile( void )
{
   char filename[ 128 ];

   GetPathFromEnvironment( filename, ApogeePath, GUSMIDIINIFILE );
   if (access (filename, F_OK) != 0)
      {
      int lump;

      lump=W_GetNumForName("gusmidi");

      SaveFile (filename, W_CacheLumpNum(lump,PU_CACHE), W_LumpLength(lump));
      }
}

//***************************************************************************
//
// MU_GetNumForType - returns number of song in rottsongs of specific type
//
//***************************************************************************
int MU_GetNumForType ( int type )
{
   int i;

   for (i=0;i<MAXSONGS;i++)
      {
      if (rottsongs[i].songtype == type)
         return i;
      }
   Error("MU_GetNumForType: could not find song type in list\n");
   return -1;
}


//***************************************************************************
//
// MU_PlaySong - Play a specific song number
//
//***************************************************************************

void MU_PlaySong ( int num )
{
   if (MU_Started==false)
      return;

   if (num<0)
      return;

   if (num>=MAXSONGS)
      Error("Song number out of range\n");

   MU_StopSong();

   lastsongnumber=num;

   currentsong=W_CacheLumpName(rottsongs[num].lumpname,PU_STATIC);
   if (rottsongs[num].loopflag == loop_yes)
      MUSIC_PlaySong(currentsong,MUSIC_LoopSong);
   else
      MUSIC_PlaySong(currentsong,MUSIC_PlayOnce);
   MU_SetVolume (MUvolume);
}

//***************************************************************************
//
// MU_StopSong - Play a specific song number
//
//***************************************************************************

void MU_StopSong ( void )
{
   if (MU_Started==false)
      return;

   MUSIC_StopSong ();
   if (currentsong)
      {
      W_CacheLumpName(rottsongs[lastsongnumber].lumpname,PU_CACHE);
      currentsong=0;
      }
}

//***************************************************************************
//
// MU_GetSongNumber - get current song number
//
//***************************************************************************

int MU_GetSongNumber ( void )
{
   return lastsongnumber;
}


//***************************************************************************
//
// MU_FadeToSong - Fade to a specific song in a certain time
//
//***************************************************************************

void MU_FadeToSong ( int num, int time )
{
   int t;

   if (MU_Started==false)
      return;

   MU_FadeOut(time>>1);

   while (MU_FadeActive())
      {
      t=ticcount;
      while (ticcount==t) {}
      }

   MU_FadeIn (num,time>>1);
}

//***************************************************************************
//
// MU_FadeIn - Fade in
//
//***************************************************************************

void MU_FadeIn ( int num, int time )
{
   if (MU_Started==false)
      return;

   MUSIC_SetVolume(0);
   MU_PlaySong ( num );
   MUSIC_FadeVolume (MUvolume, time);
}

//***************************************************************************
//
// MU_FadeOut - Fade out
//
//***************************************************************************

void MU_FadeOut ( int time )
{
   if (MU_Started==false)
      return;
   if (!MUSIC_SongPlaying())
      {
#if (DEVELOPMENT == 1)
      SoftError("Called FadeOut with no song playing\n");
#endif
      return;
      }
   MUSIC_FadeVolume(0,time);
}


//***************************************************************************
//
// MU_StartSong - Start a context sensitive song
//
//***************************************************************************

void MU_StartSong ( int songtype )
{
   int songnum;

   if (MU_Started==false)
      return;

   MU_StopSong();

   songnum = MU_GetNumForType ( songtype );
   switch (songtype)
      {
      case song_level:
         if (IsChristmas())
            {
            songnum = MU_GetNumForType ( song_christmas );
            }
         else
            {
            songnum += GetSongForLevel ();
            }
         break;
      }
   MU_PlaySong (songnum);
}

//***************************************************************************
//
// MU_StoreSongPostition - Save off Song Position
//
//***************************************************************************

void MU_StoreSongPosition ( void )
{
   if (MU_Started==false)
      return;
   PositionStored=true;
   storedposition=MUSIC_GetPosition();
}

//***************************************************************************
//
// MU_RestoreSongPostition - Save off Song Position
//
//***************************************************************************

void MU_RestoreSongPosition ( void )
{
   if (MU_Started==false)
      return;
   if (PositionStored==false)
      return;
   PositionStored=false;

   MUSIC_SetPosition(storedposition);
}

//***************************************************************************
//
// MU_GetStoredPostition - Get Stored song Position
//
//***************************************************************************

int MU_GetStoredPosition ( void )
{
   if (PositionStored)
      return storedposition;
   else
      return -1;
}

//***************************************************************************
//
// MU_SetStoredPostition - Get Stored song Position
//
//***************************************************************************

void MU_SetStoredPosition ( int position )
{
   if (MU_Started==false)
      return;
   if (position==-1)
      return;
   PositionStored=true;
   storedposition=position;
}



//***************************************************************************
//
// MU_GetSongPostition - Get Song Position
//
//***************************************************************************

int MU_GetSongPosition ( void )	
{
   if (MU_Started==false)
      return 0;
   return MUSIC_GetPosition();
}

//***************************************************************************
//
// MU_SetSongPostition - Set Song Position
//
//***************************************************************************

void MU_SetSongPosition ( int position )
{
   if (MU_Started==false)
      return;
   MUSIC_SetPosition(position);
}

//***************************************************************************
//
// MU_SaveMusic
//
//***************************************************************************

void MU_SaveMusic (byte ** buf, int * size)
{
   int unitsize;
   byte *ptr;
   int vsize;
   int i;

   //
   // Size
   //

   unitsize=0;

	unitsize+=sizeof(i);
	unitsize+=sizeof(i);
	unitsize+=sizeof(i);


   *size = unitsize;
   *buf = (byte *) SafeMalloc (*size);

   ptr = *buf;

   i=MU_GetSongNumber();
   if (rottsongs[i].songtype == song_menu)
      {
      i = MU_GetNumForType ( song_level );
      if (IsChristmas())
         {
         i = MU_GetNumForType ( song_christmas );
         }
      else
         {
         i += GetSongForLevel ();
         }
      vsize=sizeof(i);
      memcpy(ptr,&i,vsize);
      ptr+=vsize;

      i=MU_GetStoredPosition();
      vsize=sizeof(i);
      memcpy(ptr,&i,vsize);
      ptr+=vsize;

      i=-1;
      vsize=sizeof(i);
      memcpy(ptr,&i,vsize);
      ptr+=vsize;
      }
   else
      {
      vsize=sizeof(i);
      memcpy(ptr,&i,vsize);
      ptr+=vsize;

      i=MU_GetSongPosition();
      vsize=sizeof(i);
      memcpy(ptr,&i,vsize);
      ptr+=vsize;

      i=MU_GetStoredPosition();
      vsize=sizeof(i);
      memcpy(ptr,&i,vsize);
      ptr+=vsize;
      }
}


//***************************************************************************
//
// MU_LoadMusic
//
//***************************************************************************

void MU_LoadMusic (byte * buf, int size)
{
   int unitsize;
   byte *ptr;
   int i;
   int songnumber;
   boolean differentsong=false;
   int vsize;

   //
   // Size
   //

   unitsize=0;

	unitsize+=sizeof(i);
	unitsize+=sizeof(i);
	unitsize+=sizeof(i);

   if (size!=unitsize)
      Error("LoadMusic: Different number of parameters\n");

   ptr  = buf;

   vsize=sizeof(songnumber);
   memcpy(&songnumber,ptr,vsize);
   ptr+=vsize;
   if (MU_GetSongNumber () != songnumber)
      {
      MU_PlaySong(songnumber);
      differentsong=true;
      }

   vsize=sizeof(i);
   memcpy(&i,ptr,vsize);
   ptr+=vsize;
   if (differentsong==true)
      {
      MU_SetSongPosition(i);
      }

   vsize=sizeof(i);
   memcpy(&i,ptr,vsize);
   ptr+=vsize;
   MU_SetStoredPosition(i);

   // Check if game was saved with NOSOUND

   if (MU_GetSongNumber () != songnumber)
      {
      MU_StartSong ( song_level );
      }
}
